package com.mrogrape.util;


/**
 * 包含实际编程中的一些实用的处理字符串的方法。
 * 
 * 
 * @author Ice Spring
 * @version 1.3 20121029
 *
 */
public class StringUtils {

	/**
	 * 空字符串。
	 */
	public static final String BLANK = "";

	/**
	 * 一个空白字符。
	 */
	public static final String SPACE = " ";

	/**
	 * 换行符.
	 */
	public static final String LINE_SEPARATOR;

	// 初始化常量。
	static {

		LINE_SEPARATOR = System.getProperty("line.separator");
	}

	/**
	 * 将一些常见的无法在HTML页中正常显式或转义的字符转义为HTML标记。
	 * 
	 * <p>
	 * 一般在HTML页上显示一个来自数据库或用户输入的字符串时，浏览器一般不会理会其中的转义字符
	 * （如'\n','\t'等）。而当字符串中含有HTML中的标记时，浏览器却会对它们进行转义。这就导致了实际的
	 * 格式和显示的效果不一致。此方法可以将字符串中常见的的转义字符和特殊字符转义为HTML中的标记，以
	 * 保证格式文本在HTML中被浏览器正确解析。
	 * 
	 * <p>
	 * <b>可转义的字符如下</b>
	 * <br>"&amp;","&lt","&gt","\n","\r\n","&nbsp;","&copy;"
	 * 
	 * @param source 需要转义的HTML文本
	 * @return 转义后的结果
	 */
	public static String escapeHTML(String source)
	{

		source=source.replace("&", "&amp;");
		source=source.replace("<", "&lt");
		source=source.replace(">", "&gt");
		source=source.replace("\r\n", "<br>");
		source=source.replace("\n", "<br>");
		source=source.replace(" ", "&nbsp;");
		source=source.replace("\u00A9","&copy;");

		return source;

	}

	/**
	 * 将字符串中的转义字符进行转义。
	 * 
	 * <p>
	 * 将显式的转义字符即转义字符的字面表示形式转义。如“"\n"”这个字符串会被转义为实际的
	 * 换行符“'\n'”（注意引号的不同）。这个字面指的是被表示出来的，看到的（转义字符实际上是不可见的）。
	 * 转换后的字符串长度可能会变小。因为、用来表示转义字符的字符串一般都是由多个字符表示的，如“"\n"”
	 * 有两个字符‘'\'’和‘'n'’，但其代表的是转义 字符“'\n'”。
	 * 
	 * @param str 需要进行字面转义的字符串。
	 * @return 转义后的字符串。
	 */
	public static String escape(String str){
		char[] in = str.toCharArray();
		int off = 0;
		int len = in.length;
		int end = len;
		char[] out = new char[len];
		int outLen = 0;
		char aChar = 0;
		while(off < end){
			aChar = in[off++];

			//found the escape code flag
			if(aChar == '\\'){
				aChar = in[off++];
				//is a unicode
				if(aChar == 'u'){
					// Read the xxxx
					int value=0;
					for (int i=0; i<4; i++) {
						aChar = in[off++];  
						switch (aChar) {
							case '0': case '1': case '2': case '3': case '4':
							case '5': case '6': case '7': case '8': case '9':
								value = (value << 4) + aChar - '0';
								break;
							case 'a': case 'b': case 'c':
							case 'd': case 'e': case 'f':
								value = (value << 4) + 10 + aChar - 'a';
								break;
							case 'A': case 'B': case 'C':
							case 'D': case 'E': case 'F':
								value = (value << 4) + 10 + aChar - 'A';
								break;
							default:
								throw new IllegalArgumentException(
								"Malformed \\uxxxx encoding.");
						}
					}
					out[outLen++] = (char)value;
				}else {
					if (aChar == 't') {
						aChar = '\t';
					} else if (aChar == 'r') {
						aChar = '\r';
					} else if (aChar == 'n') {
						aChar = '\n';
					} else if (aChar == 'f') {
						aChar = '\f';
					} 
					out[outLen++] = aChar;
				}
			} else {
				out[outLen++] = aChar;
			}
		}

		return new String (out, 0, outLen);
	}

	/**
	 * 对字符串进行反转义，即将转义字符转换为其字面表示形式。
	 * 
	 * <p>
	 * 将字符串中的转义字符反转义为其字面表示形式，如“'\n'”会被反转义为“"\n"”。
	 * 
	 * 
	 * @param theString 需要进行反转义的字符串。
	 * @param escapeSpace 是否转义空格为“"\ "”。
	 * @param escapeUnicode 是否转义unicode字符。功能类似于{@link #native2ascii(String)}。
	 * @return 反转义后的字符串
	 */
	public static String unescape(String theString, boolean escapeSpace,
			boolean escapeUnicode) {
		int len = theString.length();
		int bufLen = len * 2;
		if (bufLen < 0) {
			bufLen = Integer.MAX_VALUE;
		}
		StringBuilder outBuffer = new StringBuilder(bufLen);

		for (int x = 0; x < len; x++) {
			char aChar = theString.charAt(x);
			// Handle common case first, selecting largest block that
			// avoids the specials below
			if ((aChar > 61) && (aChar < 127)) {
				if (aChar == '\\') {
					outBuffer.append('\\');
					outBuffer.append('\\');
					continue;
				}
				outBuffer.append(aChar);
				continue;
			}
			switch (aChar) {
				case ' ':
					if (x == 0 || escapeSpace) {
						outBuffer.append('\\');
					}
					outBuffer.append(' ');
					break;
				case '\t':
					outBuffer.append('\\');
					outBuffer.append('t');
					break;
				case '\n':
					outBuffer.append('\\');
					outBuffer.append('n');
					break;
				case '\r':
					outBuffer.append('\\');
					outBuffer.append('r');
					break;
				case '\f':
					outBuffer.append('\\');
					outBuffer.append('f');
					break;
				case '=': // Fall through
				case ':': // Fall through
				case '#': // Fall through
				case '!':
					//outBuffer.append('\\');
					outBuffer.append(aChar);
					break;
				default:
					if (((aChar < 0x0020) || (aChar > 0x007e)) & escapeUnicode) {
						outBuffer.append('\\');
						outBuffer.append('u');
						outBuffer.append(StringUtils.toHex((aChar >> 12) & 0xF));
						outBuffer.append(StringUtils.toHex((aChar >> 8) & 0xF));
						outBuffer.append(StringUtils.toHex((aChar >> 4) & 0xF));
						outBuffer.append(StringUtils.toHex(aChar & 0xF));
					} else {
						outBuffer.append(aChar);
					}
			}
		}
		return outBuffer.toString();
	}

	/**
	 * 提供类似于JDK工具native2ascii的功能：将字符串中的字符本地化为其unicode转义字符。
	 * 
	 * @param str 需要进行本地化的字符串。
	 * @return 本地化后的字符串。
	 */
	public static String native2ascii(String theString) {
		int len = theString.length();
		int bufLen = len * 2;
		if (bufLen < 0) {
			bufLen = Integer.MAX_VALUE;
		}
		StringBuilder outBuffer = new StringBuilder(bufLen);

		for (int x = 0; x < len; x++) {
			char aChar = theString.charAt(x);
			if ((aChar > 61) && (aChar < 127)) {
				outBuffer.append(aChar);
				continue;
			}
			if (((aChar < 0x0020) || (aChar > 0x007e))) {
				outBuffer.append('\\');
				outBuffer.append('u');
				outBuffer.append(StringUtils.toHex((aChar >> 12) & 0xF));
				outBuffer.append(StringUtils.toHex((aChar >> 8) & 0xF));
				outBuffer.append(StringUtils.toHex((aChar >> 4) & 0xF));
				outBuffer.append(StringUtils.toHex(aChar & 0xF));
			} else {
				outBuffer.append(aChar);
			}

		}
		return outBuffer.toString();

	}
	
	/**
	 * 如果提供的字符串为null，则将其转化为空白字符串对象，否则不做处理。
	 * 
	 * @param str 需要转化的字符串
	 * @return 转换后的字符串。
	 */
	public static String nullToEmpty(String str){
		
		return str == null ? StringUtils.BLANK : str;
	}

	/**
	 * Convert a nibble to a hex character
	 * @param	nibble	the nibble to convert.
	 */
	private static char toHex(int nibble) {
		return StringUtils.hexDigit[(nibble & 0xF)];
	}

	/** A table of hex digits */
	private static final char[] hexDigit = { '0', '1', '2', '3', '4', '5', '6',
		'7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };

	/**
	 * 判断一个字符串是否不为数字(Not A Number)。
	 * 
	 * @param aStr 需要进行判断的字符串。
	 * @return 表示是否为数字的boolean值：
	 * 			<ul>
	 * 			<li>true  : 字符串不是数字。
	 * 			<li>false : 字符串是数字。
	 * 			</ul>
	 */
	public static boolean isNaN(String aStr) {

		return !StringUtils.isNumber(aStr);
	}

	/**
	 * 判断一个字符串是否是一个数字。
	 * 
	 * @param  aStr 需要进行判断的字符串。
	 * @return 表示是否为数字的boolean值：
	 * 			<ul>
	 * 			<li>true  : 字符串是数字。
	 * 			<li>false : 字符串不是数字。
	 * 			</ul>
	 */
	public static boolean isNumber(String aStr){

		if(StringUtils.isEmpty(aStr)){
			return false;
		}

		char[] buf = aStr.toCharArray();
		int dot = 0;

		for(char c : buf){
			switch(c){
				case '0': case '1': case '2': case '3': case '4':
				case '5': case '6': case '7': case '8': case '9':
					continue;
				case '.':
					dot ++ ;
					break;
				default :
					return false;
			}
		}

		return dot <= 1;
	}


	/**
	 * 判断两个字符串是否相等。
	 * 
	 * @param str1 字符串1
	 * @param str2 字符串2
	 * @return 字符串是否相等的boolean值：
	 * 			<ul>
	 * 			<li>true  : 字符串相等
	 * 			<li>false : 字符串不相等。
	 * 			</ul>
	 */
	public static boolean isEquals(String str1,String str2){

		if(str1 == str2){
			return true;
		}

		if(str1!=null){
			return str1.equals(str2);
		}else {
			return str2.equals(str1);
		}

	}

	/**
	 * 判断一个字符串是为空。
	 * 
	 * <p>
	 * 一个字符串需要满足以下两个条件的任意一个，此方法就会返回true：
	 * <ol type='1'>
	 * <li>为null.
	 * <li>长度为0.
	 * </ol>
	 * 
	 * @param aStr 需要进行判断的字符串。
	 * 
	 * @return 表示字符串是否非空的boolean值：
	 * 			<ul>
	 * 			<li>true  : 为空。
	 * 			<li>false : 非空。
	 * 			</ul>
	 * @see #isNotEmpty(String)
	 */
	public static boolean isEmpty(String aStr) {

		return aStr == null || aStr.isEmpty();
	}

	/**
	 * 判断一个字符串在执行过“trim”操作后是否为空。
	 * 
	 * @param aStr 需要进行判断的字符串。
	 * @return 字符串是否在trim后为空的标记，如果在trim后为空则返回true
	 */
	public static boolean isEmptyAfterTrim(String aStr){

		return aStr == null || aStr.trim().isEmpty();
	}

	/**
	 * 判断一个字符串是否非空。
	 * 
	 * <p>
	 * 一个字符串需要满足以下两个条件，此方法才会返回true：
	 * <ol type='1'>
	 * <li>不为null.
	 * <li>长度(string.lenght)不为0.
	 * </ol>
	 * 
	 * @param aStr 需要进行判断的字符串。
	 * 
	 * @return 表示字符串是否非空的boolean值：
	 * 			<ul>
	 * 			<li>true  : 非空。
	 * 			<li>false : 为空。
	 * 			</ul>
	 * @see #isEmpty(String)
	 */
	public static boolean isNotEmpty(String aStr) {

		return aStr != null && !aStr.isEmpty();
	}

	/**
	 * 判断一个字符串是否在执行“trim”操作后不为空。
	 * 
	 * @param aStr 判断
	 * @return
	 */
	public static boolean isNotEmptyAfterTrim(String aStr){

		return aStr != null && !aStr.trim().isEmpty();
	}

	/**
	 * 在右侧截取字符串。
	 * 
	 * @see #cutString(String, int, String);
	 */
	public static final String CUTSTRING_RIGHT = "Right";

	/**
	 * 在右侧截取字符串。
	 * 
	 * @see #cutString(String, int, String);
	 */
	public static final String CUTSTRING_LEFT = "Left";

	/**
	 * 在右侧截取字符串。
	 * 
	 * @see #cutString(String, int, String);
	 */
	public static final String CUTSTRING_MIDDLE = "Middle";

	/**
	 * 通过截断显示的方法使一个较长的字符串得以在一定长度内显示。
	 * 
	 * <p>
	 * 在一个有限的空间中显示一个超出显示空间的字符串时，我们常常需要截取显示。
	 * 此方法实现此功能并提供了以下三种截取方案。
	 * <ul>
	 * <li>CUTSTRING_RIGHT  : 从右侧截取。如果发生截取，将剩余字符串的最后三位以"..."表示。
	 * <li>CUTSTRING_LEFT   : 从左侧截取。如果发生截取，将剩余字符串的前三位以"..."表示 。
	 * <li>CUTSTRING_MIDDLE : 从中间截取。如果发生截取，将剩余字符串中央三位以"..."表示。
	 * </ul>
	 * 
	 * @param str 需要截取的字符串。
	 * @param maxLength 输出的最大长度。
	 * @param align 截取方案
	 * @return 处理后的字符串。
	 */
	public static String cutString(String str, int maxLength, String align) {	
		if(str == null){
			return null;
		}

		if(maxLength < 0){
			throw new IllegalArgumentException(maxLength + " < 0!");
		}

		int len = str.length();

		if (len <= maxLength) {
			return str;
		}

		if (StringUtils.CUTSTRING_LEFT.equals(align)) {
			if(maxLength < 3){
				return str.substring(len - maxLength);
			}
			return "..." + str.substring(len - maxLength + 3);

		} else if (StringUtils.CUTSTRING_MIDDLE.equals(align)) {

			boolean isEven = maxLength%2 == 0;

			String left = str.substring(0,maxLength / 2);
			String right = str.substring(len-maxLength / 2 - (isEven?0:1));

			if(maxLength<3){
				return left + right;
			}
			return left.substring(0,left.length()-1)+"..."+right.substring(2);


		} else if (StringUtils.CUTSTRING_RIGHT.equals(align)) {
			if(maxLength < 3){
				return str.substring(0,maxLength);
			}

			return str.substring(0, maxLength - 3) + "...";

		}

		return str;
	}

	/**
	 * 右对齐。
	 */
	public static final int ALIGN_RIGHT = -1;

	/**
	 * 左对齐。
	 */
	public static final int ALIGN_LEFT = 1;

	/**
	 * 居中对齐。
	 */
	public static final int ALIGN_MIDDLE = 0;

	/**
	 * 修整字符串使其长度相同。
	 * 
	 * <p>
	 * 此方法主要用于将一个较短的字符串使用空格填充至指定长度。如果指定的字符串长度
	 * 超长，则不作任何改动。
	 * 
	 * @param aStr      需要修整的字符串,可以为空。
	 * @param minLength 期望的最小长度。
	 * @param align     字符对齐方式。
	 * 
	 * @return 修整过后的字符串。
	 */
	public static String fixString(String aStr, int minLength, int align) {

		return StringUtils.fixString(aStr, minLength, align,
				StringUtils.SPACE.toCharArray()[0]);
	}

	/**
	 * 修整字符串使其长度相同。
	 * 
	 * <p>
	 * 此方法主要用于将一个较短的字符串使用指定字符填充至指定长度。如果指定的字符串长度
	 * 超长，则不作任何改动。
	 * 
	 * @param aStr      需要修整的字符串,可以为空。
	 * @param minLength 期望的最小长度。
	 * @param align     字符对齐方式。
	 * @param fill      填充使用的字符。
	 * 
	 * @return 修整过后的字符串。
	 */
	public static String fixString(String aStr, int minLength, int align,char fill){

		if(aStr == null||minLength <= aStr.length()){
			return aStr;
		}

		int length = aStr.length();
		int missing = minLength - length;
		String fillChar = String.valueOf(fill);
		String missStr = StringUtils.repeatString(fillChar, missing);
		if (align == StringUtils.ALIGN_LEFT) {
			return aStr + missStr;
		} else if (align == StringUtils.ALIGN_MIDDLE) {
			int left = missing / 2;
			int right = missing / 2 + missing % 2;
			return StringUtils.repeatString(fillChar, left) + aStr
			+ StringUtils.repeatString(fillChar, right);
		} else if (align == StringUtils.ALIGN_RIGHT) {
			return missStr + aStr;
		} else {
			throw new IllegalArgumentException("Not a available align mode.");
		}

	}

	/**
	 * 安全的{@link java.lang.String#toString()}的实现，可以接受任何值（包括null）。
	 * 
	 * <p>
	 * 在接受null值时将直接返回null,否则返回该对象的toString()。
	 * 
	 * @param obj 任意对象，可以为null.
	 * @return 该对象toString的值。
	 */
	public static String toString(Object obj){

		return obj == null ? null : obj.toString();
	}

	/**
	 * 根据元字符串和重复次数生成连续的重复字符串。
	 * 
	 * <p>
	 * 此方法将元字符串按照指定的次数重复添加至同一个字符串构建器来完成操作。可用于
	 * 特殊情况下字符串填充的需求。
	 * <br>
	 * 如果指定的元字符串为空（null,或者""），则直接返回空字符串（""）。
	 * 如果count数小于1也返回空字符串。
	 * 
	 * @param aStr 元字符串。
	 * @param count 重复计数。
	 * @return 生成的字符串。
	 */
	public static String repeatString(String aStr, int count){

		if(StringUtils.isEmpty(aStr)){
			return StringUtils.BLANK;
		}

		StringBuilder result = new StringBuilder();
		for(int i =0; i < count; i++){
			result.append(aStr);
		}

		return result.toString();

	}
	
	/**
	 * 判断两个字符串在不区分大小写的情况下是否相等。
	 * 
	 * @param str1 字符串1，可以接收null。
	 * @param str2 字符串2，可以接收null。
	 * @return 指示两个字符串是否相等的boolean值，true的场合为相等。
	 */
	public static boolean isEqualsIgnoreCase(String str1, String str2){
		
		if(str1 == null){
			return str2 == null;
		}
		
		return str1.equalsIgnoreCase(str2);
	}
	
	/**
	 * 判断两个字符串在不区分大小写的情况下是否<b>不</b>相等。
	 * 
	 * @param str1 字符串1，可以接收null。
	 * @param str2 字符串2，可以接收null。
	 * @return 指示两个字符串是否相等的boolean值，true的场合为<b>不</b>相等。
	 */
	public static boolean isNotEqualsIgnoreCase(String str1, String str2){
		
		return !isEqualsIgnoreCase(str1, str2);
	}


	private StringUtils(){
		// 阻止实例化。
	}
}
